#include "tier_app.h"
#include "../dll.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

static int pkt_maxs; /* Tamanho maximo de um pacote */

/* Configura o tamanho maximo do pacote */
void app_set_pkt_maxs()
{
   pkt_maxs = dll_mtu() - APP_H;
   return;
}

/* Retorna o tamanho maximo da parte de dados */
int app_get_ptk_maxs()
{
    return pkt_maxs;
}

/* Deve ser chamada ao iniciar o cliente\servidor */
packet_t app_init()
{
    if ( dll_init() < 0 )
    {
        perror("dll_init");
        exit(1);
    }
    app_set_pkt_maxs();
    return (packet_t) malloc ( (pkt_maxs + APP_H + 1) * sizeof(char) );
}

/* Deve ser chamada no fim do teste */
void app_end( packet_t *pkt )
{
    usleep(50000); /*Da um tempo para a camada de enlace terminar*/
    free(*pkt);
    return;
}

/* Envia o pacote pkt de tamanho msglen */
void app_send( packet_t *pkt, int msglen )
{
    if ( msglen > dll_mtu() )
    {
        fprintf(stderr,"Mensagem muito longa: %d bytes (MTU: %d)\n", msglen, dll_mtu());
        exit(1);
	}
    assert(dll_send(*pkt, msglen) == msglen);
   return;
}

/* Recebe o pacote pkt e retorna a quantidade de bytes lidos */
int app_recv( packet_t *pkt )
{
	assert(*pkt);
	int msglen = dll_recv(*pkt, dll_mtu());
	if( msglen < 0 )
    {
		fprintf(stderr,"Problema com dll_recv\n");
		exit(1);
	}
	return msglen;
}

/* Retorna o caractere do cabecalho */
char app_get_h( packet_t *pkt )
{
	return *pkt[0];
}

/* Preenche o cabelho com o caractere h */
void app_set_h( packet_t *pkt, char h )
{
    *pkt[0] = h;
    return;
}

/* Envia mensagem de erro - Epecifico para tentativa de abrir um arquivo */
void app_fileerror( char *errmsg, packet_t* pkt )
{
	int msglen = strlen(errmsg)+1;
	memcpy(*pkt + APP_H, errmsg, msglen+1);
	app_set_h(pkt, 'e');
	msglen += APP_H;
	perror(errmsg);
	app_send(pkt, msglen);
    return;
}

/* Recebe um arquivo do cliente/servidor */
void app_cs_recv( packet_t *pkt, FILE **f )
{
	int msglen;
	char h;

	msglen = app_recv(pkt);
	h = app_get_h(pkt);

	while( h == 'a' )
	{
		pkt[msglen-1] = 0;
		fwrite(*pkt + APP_H, sizeof(char), msglen - APP_H, *f);
		msglen = app_recv(pkt);
		h = app_get_h(pkt);
	}

	if( h == 'e' )
	{
		pkt[msglen] = '\0';
		fprintf(stderr, "Erro: %s\n", *pkt + APP_H);
		fclose(*f);
		app_end(pkt);
		exit(1);
	}
    else if( h == 'f'){
        /*Utilizado para informar a outra parte que tudo ja foi recebido*/
        app_set_h(pkt, 'f');
        app_send(pkt, APP_H);
        /*Utilizado para informar ao enlace que ja pode terminar*/
        app_send(pkt, FINISH);

    }
    else{
        fprintf(stderr, "Erro: Cabecalho invalido\n");
        fclose(*f);
        app_end(pkt);
        exit(1);
    }

	if( (msglen - APP_H) > 0 )
	{
		fwrite(*pkt + APP_H, sizeof(char), msglen - APP_H, *f);
	}
	return;
}

/* Envia um arquivo para o cliente/servidor */
void app_cs_send( packet_t *pkt, FILE **f )
{
	int msglen;
	int ptk_maxs = app_get_ptk_maxs();
    
	app_set_h(pkt, 'a');
	msglen = fread(*pkt + APP_H, sizeof(char), ptk_maxs, *f);

	while( msglen )
    {
		app_send(pkt, msglen + APP_H);
		msglen = fread(*pkt + APP_H, sizeof(char), ptk_maxs, *f);
	}

	if( feof(*f) )
    {
		app_set_h(pkt, 'f');
		app_send(pkt, APP_H);
        /* Recebe confirmacao para terminar */
        app_recv(pkt);
        assert(app_get_h(pkt) == 'f');
        /* Avisa a camada de enlace que pode terminar */
        app_send(pkt, FINISH);
	}
	else if( ferror(*f) )
    {
		perror("Erro na leitura do arquivo\n");
		app_set_h(pkt, 'e');
		sprintf(*pkt + APP_H, "Erro na leitura do arquivo\n");
		msglen = strlen(*pkt);
		app_send(pkt, msglen + APP_H);
		fclose(*f);
		app_end(pkt);
		exit(1);
	}
    return;
}
